/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
var http = require('http');
var url = require("url");
var path = require("path");
var fs = require("fs");

var ttransport = require('./transport');
var TBinaryProtocol = require('./protocol').TBinaryProtocol;

/**
 * @class
 * @name StaticHttpThriftServerOptions
 * @property {string} staticFilePath - Path to serve static files from, default
 *                                is ".", use "" to disable static file service.
 * @property {object} services - An object hash mapping service URIs to 
 *                               ThriftServiceOptions objects.
 * @see {@link ThriftServiceOptions}
 */

/**
 * @class
 * @name ThriftServiceOptions
 * @property {object} transport - The layered transport to use (defaults to none).
 * @property {object} protocol - The Thrift Protocol to use (defaults to TBinaryProtocol).
 * @property {object} cls - The Thrift Service class generated by the IDL Compiler for the service.
 * @property {object} handler - The handler methods for the Thrift Service.
 */

/** 
 * Creates a Thrift server which can serve static files and/or one or
 * more Thrift Services. 
 * @param {StaticHttpThriftServerOptions} - The server configuration.
 * @returns {object} - The Thrift server.
 */
exports.createStaticHttpThriftServer = function(options) {
  //Set the static dir to serve files from
  var baseDir = typeof options.staticFilePath != "string" ? process.cwd() : options.staticFilePath;

  //Setup all of the services
  var services = options.services;
  for (svc in services) {
    var svcObj = services[svc];
    var processor = svcObj.cls.Processor || svcObj.cls;
    svcObj.processor = new processor(svcObj.handler);
    svcObj.transport = svcObj.transport ? svcObj.transport : ttransport.TBufferedTransport;
    svcObj.protocol = svcObj.protocol ? svcObj.protocol : TBinaryProtocol;
  }

  //Handle POST methods
  function processPost(request, response) {
    var uri = url.parse(request.url).pathname;
    var svc = services[uri];
    if (!svc) {
      //Todo: add support for non Thrift posts
      response.writeHead(500);
      response.end();
      return;
    }

    request.on('data', svc.transport.receiver(function(transportWithData) {
      var input = new svc.protocol(transportWithData);
      var output = new svc.protocol(new svc.transport(undefined, function(buf) {
        try {
          response.writeHead(200);
          response.end(buf);
        } catch (err) {
          response.writeHead(500);
          response.end();
        }
      }));

      try {
        svc.processor.process(input, output);
        transportWithData.commitPosition();
      }
      catch (err) {
        if (err instanceof ttransport.InputBufferUnderrunError) {
          transportWithData.rollbackPosition();
        }
        else {
          response.writeHead(500);
          response.end();
        }
      }
    }));
  }

  //Handle GET methods
  function processGet(request, response) {
    //An empty string base directory means do not serve static files
    if ("" == baseDir) {
      response.writeHead(404);
      response.end();
      return;      
    }
    //Locate the file requested
    var uri = url.parse(request.url).pathname;
    var filename = path.join(baseDir, uri);
    path.exists(filename, function(exists) {
      if (!exists) {
        response.writeHead(404);
        response.end();
        return;
      }
     
      if (fs.statSync(filename).isDirectory()) {
        filename += '/index.html';
      }
     
      fs.readFile(filename, "binary", function(err, file) {
        if (err) {
          response.writeHead(500);
          response.end(err + "\n");
          return;
        }
     
        response.writeHead(200);
        response.write(file, "binary");
        response.end();
      });
    });
  }

  //Return the server
  return http.createServer(function(request, response) {
    if (request.method === 'POST') {
      processPost(request, response);
    } else if (request.method === 'GET') {
      processGet(request, response);
    } else {
      response.writeHead(500);
      response.end();
    }
  });
};

